package com.bringspring.common.filter;

import com.alibaba.fastjson.JSONObject;
import com.bringspring.common.annotation.NotCheckLogin;
import com.bringspring.common.base.ActionResult;
import com.bringspring.common.base.ActionResultCode;
import com.bringspring.common.base.UserInfo;
import com.bringspring.common.config.ConfigValueUtil;
import com.bringspring.common.database.data.DataSourceContextHolder;
import com.bringspring.common.license.client.LicenseVerify;
import com.bringspring.common.license.license.CustomLicenseManager;
import com.bringspring.common.license.license.model.LicenseCheckModel;
import com.bringspring.common.util.CacheKeyUtil;
import com.bringspring.common.util.JsonUtil;
import com.bringspring.common.util.Md5Util;
import com.bringspring.common.util.RedisUtil;
import com.bringspring.common.util.ServletUtils;
import com.bringspring.common.util.StringUtils;
import com.bringspring.common.util.UserProvider;
import com.bringspring.common.util.context.SpringContext;
import com.bringspring.common.util.jwt.JwtUtil;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * @author 开发平台组
 * @version V1.0.0
 * @copyright 股份有限公司
 * @date 2021/3/16 8:58
 */
public class TokenInterceptor implements HandlerInterceptor {

    private UserProvider userProvider;
    private ConfigValueUtil configValueUtil;
    private RedisUtil redisUtil;

    private void init() {
        userProvider = SpringContext.getBean(UserProvider.class);
        configValueUtil = SpringContext.getBean(ConfigValueUtil.class);
        redisUtil = SpringContext.getBean(RedisUtil.class);
    }

    /**
     * 在请求处理之前进行调用（Controller方法调用之前）
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        init();
        String method = request.getMethod();
        if ("OPTIONS".equals(method)) {
            return true;
        }


        LicenseVerify licenseVerify = new LicenseVerify();
        //校验证书是否有效
        boolean verifyResult = licenseVerify.verify();
        if(!verifyResult){
            ActionResult result = ActionResult.fail(ActionResultCode.LicenseError.getCode(), ActionResultCode.LicenseError.getMessage());
            CustomLicenseManager customLicenseManager=new CustomLicenseManager();
            LicenseCheckModel model = customLicenseManager.getServerInfos();
            String pcCode=Md5Util.getStringMd5(JSONObject.toJSONString(model));
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("machineCode",pcCode);
            jsonObject.put("isExist",false);
            result.setData(jsonObject);
            ServletUtils.renderString(response, JsonUtil.getObjectToString(result));
            return false;
        }
        //如果开启了租户，设置租户数据源。
        if (Boolean.parseBoolean(configValueUtil.getMultiTenancy())) {
            UserInfo userInfo = userProvider.get();
            DataSourceContextHolder.setDatasource(userInfo.getTenantId(),userInfo.getTenantDbConnectionString());
        }

        String token = UserProvider.getToken();
        boolean notCheckLogin =false;
        if (handler instanceof HandlerMethod){
            //验证如果添加了注解的不需要验证token
            HandlerMethod handlerMethod = (HandlerMethod)handler;
            //添加了自定义不需要验证权限的注解的方法
            notCheckLogin = handlerMethod.hasMethodAnnotation(NotCheckLogin.class);
        }
        //增加了notCheckLogin 或 测试版本可以关闭验证
        if (!notCheckLogin||"false".equals(configValueUtil.getTestVersion())) {

            return  tokenByPass(request, response, token);
        }
        return true;
    }

    /**
     * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
//        String path = request.getServletPath();
//        System.out.println("postHandle："+path);
    }

    /**
     * 在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        //如果开启了租户，清除租户设置，不然数据会乱
        if (Boolean.parseBoolean(configValueUtil.getMultiTenancy())) {
            DataSourceContextHolder.clearDatasourceType();
        }
    }

    /**
     * 重新给redis中的token设置有效时间
     *
     * @param userInfo
     */
    private void tokenTimeout(UserInfo userInfo) {
        String tenantId = StringUtils.isNotEmpty(userInfo.getTenantId()) ? userInfo.getTenantId() : "";
        String userId = userInfo.getUserId();
        String onlineInfo = tenantId + CacheKeyUtil.LOGINONLINE + userId;
        redisUtil.expire(onlineInfo, userInfo.getTokenTimeout() * 60);
        redisUtil.expire(userInfo.getId(), userInfo.getTokenTimeout() * 60);
    }

    private boolean tokenByPass(HttpServletRequest request, HttpServletResponse response, String token) {

        UserInfo userInfo = userProvider.get();
        String realToken = JwtUtil.getRealToken(token);
        //token验证
        if (StringUtils.isEmpty(realToken) || !redisUtil.exists(realToken)) {
            ActionResult result = ActionResult.fail(ActionResultCode.SessionOverdue.getCode(), ActionResultCode.SessionOverdue.getMessage());
            ServletUtils.renderString(response, JsonUtil.getObjectToString(result));
            return false;
        }
        //是否过期
        Date exp = JwtUtil.getExp(token);
        if (exp.getTime() < System.currentTimeMillis()) {
            ActionResult result = ActionResult.fail(ActionResultCode.SessionOverdue.getCode(), ActionResultCode.SessionOverdue.getMessage());
            ServletUtils.renderString(response, JsonUtil.getObjectToString(result));
            return false;
        }
        //判断是否可多人登录
        Integer singleLogin = JwtUtil.getSingleLogin(token);
        if (!"1".equals(String.valueOf(singleLogin))) {
            //是否在线
            if (!userProvider.isOnLine()) {
                ActionResult result = ActionResult.fail(ActionResultCode.SessionOffLine.getCode(), ActionResultCode.SessionOffLine.getMessage());
                redisUtil.remove(realToken);
                ServletUtils.renderString(response, JsonUtil.getObjectToString(result));
                return false;
            }
        }
        //增加在线过期时间
        tokenTimeout(userInfo);

        return true;
    }

}
